/**
 * LSA.java
 *
 * @author Philip Kwok
 */


package SSF.OS.OSPFv2;


import java.util.Vector;
import java.util.Enumeration;
import SSF.OS.*;
import SSF.Net.Util.IP_s;

// ========================== class SSF.OS.OSPFv2.LSA ======================== //
/**
 * An OSPF Link State Advertisement packet.  It extends an OSPF packet. The
 * following table shows the information that goes into the different fields
 * depending on the LSA packet type:
 *
 * <pre><code>
 * <font face="Courier, Courier New">
 * <br>TYPE                       LS Identification    Sent by          Received by
 * <br>--------------------------------------------------------------------------------------
 * <br>1 - Router link            OSPF Router ID       each router      rtrs in the same area
 * <br>2 - Network link           IP itrf address of
 * <br>                           network's dest. router
 * <br>3 - Summary link (IP net)  IP network number    Area border rtr  rtrs in the same area
 * <br>4 - Summary link (ASBR)    ASBR OSPF rtr ID     ASB rtr          rtrs in the AS
 * <br>5 - External link          IP network number    AS boundary rtr  rtrs in the AS
 * <br>--------------------------------------------------------------------------------------
 *
 * <p>In RFC 2178, but NOT implemented:
 *
 * <p>1) LS checksum
 * <br>2) length
 * </code></pre>
 */
public class LSA extends ProtocolMessage {

  // ......................... constants ........................... //

  /** Indicates a router-LSA. */
  public static final byte ROUTER_LSA          = 1;
  /** Indicates a network-LSA. */
  public static final byte NETWORK_LSA         = 2;
  /** Indicates a summary-LSA (IP network). */
  public static final byte SUMMARY_NETWORK_LSA = 3;
  /** Indicates a summary-LSA (ASBR). */
  public static final byte SUMMARY_ASBR_LSA    = 4;
  /** Indicates an AS External LSA. */
  public static final byte AS_EXTERNAL_LSA     = 5;

  /** The standard length of an LSA header. */
  public static final int HEADER_LENGTH = 20;

  // ........................ member data .......................... //

  /** The age of this Link State Advertisement. */
  public long lsAge = 0;

  /** The options field representing the capabilities of the router which
   * originates this LSA. */
  public boolean[] options;

  /** The type of this Link State Advertisement. */
  public byte lsType;

  /** The Link State Identification of this LSA. */
  public int lsID;

  /** The advertising router of this LSA. */
  public int advRouter;
  public String advrtrnh;

  /** The link state sequence number of this LSA header. */
  public int lsSeqnum;

  // --------------------------- constructor LSA --------------------------- //
  /**
   * Constructs a Link State Advertisement.
   *
   * @param seq   LSA sequence number.
   * @param opt   Capabilities supported by the described portion of the routing domain.
   * @param t     LSA type.
   * @param lsID  Link state ID (OSPF router ID).
   * @param ar    The ID of the advertising router.
   * @param arnh  The NHI prefix address of the advertising router.
   * @param cont  The LSA list to send.
   */
  public LSA(boolean[] opt, byte t, int id, int ar, String arnh, int seq) {
    options = (boolean[])opt.clone();
    lsType = t;
    lsID = id;
    advRouter = ar;
    advrtrnh = arnh.substring(0);
    lsSeqnum = seq;
  }

  // ------------------------------ LSA.getKey ----------------------------- //
  /** creates a key from the values of lsTyp, lsID and advRouter which uniquely 
   *  identifies this LSA. */
  public String getKey() {
    String key;
    key = lsType + " " + IP_s.IPtoString(lsID) + " " + IP_s.IPtoString(advRouter);
    return key;
  }

  // ----------------------------- LSA.copyHeader ----------------------------- //
  /** Return an exact copy of this LSA header. */
  public LSA copyHeader() {
    LSA lsa = new LSA(options, lsType, lsID, advRouter, advrtrnh, lsSeqnum);
    lsa.lsAge = lsAge;
    return lsa;
  }

  // ------------------------------- LSA.copy ---------------------------------- //
  /** Return an exact copy of the complete LSA. */
  public ProtocolMessage copy() {
    LSA lsa = copyHeader();
    lsa.carryPayload(payload().copy());
    return lsa;
  }

  // ---------------------------- LSA.bytecount ---------------------------- //
  /** Return the total size (in bytes) of this LSA including the header. 
   *  All and only fields specified in RFC 2328 are included in calculation!!!
   */
  public int bytecount() {
    if (payload() == null) {
      return HEADER_LENGTH;
    } else {
      return (HEADER_LENGTH + payload().bytecount());
    }
  }

  // ------------------------ LSA.headerByteCount ------------------------- //
  /** Return the size (in bytes) of this Packet only, don't include the payload, if any. */
  public static int headerByteCount() {
    return HEADER_LENGTH;
  }

  // ----------------------------- LSA.toString ---------------------------- //
  public String toString() {
    String s = getStrFromType(lsType)+"   "+IP_s.IPtoString(lsID)+"          "
               +IP_s.IPtoString(advRouter)+"         "+Integer.toHexString(lsSeqnum)+"         "+lsAge;
    ProtocolMessage load = this.payload(); // could be null!!!
    switch (lsType) {
    case ROUTER_LSA:
      if (load instanceof Router_LSA) {
        LinkInfo nextLink;
        for (Enumeration e = ((Router_LSA)load).links.elements(); e.hasMoreElements(); ) {
	  nextLink = (LinkInfo)e.nextElement();
          s += "\n" + nextLink.toString();
        }
      }
    break;
    case SUMMARY_NETWORK_LSA:
      if (load instanceof Summary_LSA) {
	int mask = ((Summary_LSA)load).netMask;
        if (mask == 0) {
          s += "\n   Network Mask= 0 | Metric= " + ((Summary_LSA)load).metric;
        } else {
	  s += "\n   Network Mask= " + 
               (32 - (int)(Math.log((new Double(mask)).doubleValue() * -1.0)/Math.log(2.0))) + 
               " | Metric= " + ((Summary_LSA)load).metric;
        }
      }
    break;
    case SUMMARY_ASBR_LSA:
      if (load instanceof Summary_LSA) {
        s += "\n   Metric= " + ((Summary_LSA)load).metric;
      }
    break;
    }
    return s;
  }

  // ------------------------------ Neighbor.getStrFromType ------------------------- //
  /**
   * Converts the byte value of lsType in a more readable format. This is only used
   * for debuging purposes.
   */
  public String getStrFromType(byte type) {
    switch (type) {
    case ROUTER_LSA:
      return ("Router-LSA");
    case NETWORK_LSA:
      return ("Network-LSA");
    case SUMMARY_NETWORK_LSA:
      return ("Summary3-LSA");
    case SUMMARY_ASBR_LSA:
      return ("Summary4-LSA");
    default:
      return("Not defined!");
    }
  }

} // end class LSA




